package com.chenfan.common.extension.dict;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.ContextValueFilter;
import com.chenfan.common.extension.dict.annotation.DictTag;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * 字典翻译工具类
 *
 * @author wulg
 * @date 2021-09-11
 **/
public class DictTranslationUtils {

    private static final String SEPARATOR = ",";

    /**
     * 字典翻译 - 不支持嵌套
     * @param dictStream
     * @param sources
     * @param <T>
     * @param <R>
     */
    public static <T,R> void translation(DictStream<T> dictStream, BiFunction<List<String>,List<String>, Map<String, String>> biFunction, T... sources) {
        if(dictStream == null || biFunction== null || ArrayUtils.isEmpty(sources)) {
            return;
        }
        translation(dictStream, biFunction, Lists.newArrayList(sources));
    }

    /**
     * 字典翻译 - 不支持嵌套
     * @param sourceList
     * @param dictStream
     * @param <T>
     * @param <R>
     */
    public static <T,R> void translation(DictStream<T> dictStream, BiFunction<List<String>,List<String>, Map<String, String>> biFunction, List<T> sourceList) {
        if(dictStream == null || biFunction== null || CollectionUtils.isEmpty(sourceList)) {
            return;
        }
        List<DictStream.Target<T>> targetList = dictStream.collect();
        if(CollectionUtils.isEmpty(targetList)){
            return;
        }
        List<String> dictProfileList  = targetList.stream().map(DictStream.Target::getDictType).distinct().collect(Collectors.toList());
        List<String> dictCodeList = new ArrayList<>();
        try {
            sourceList.forEach(a -> {
                for (DictStream.Target<T> target : targetList) {
                    String sourceDictCode = valueOf(target.getFunction().apply(a));
                    if(StringUtils.isBlank(sourceDictCode)) {
                        continue;
                    }
                    String[] dictCodes = StringUtils.split(sourceDictCode, SEPARATOR);
                    if(ArrayUtils.isEmpty(dictCodes)) {
                        continue;
                    }
                    for(String dictCode : dictCodes) {
                        if(!dictCodeList.contains(dictCode)) {
                            dictCodeList.add(dictCode);
                        }
                    }
                }
            });
            Map<String, String> dictMapping = biFunction.apply(dictProfileList, dictCodeList);
            if (Objects.isNull(dictMapping)) {
                return;
            }
            sourceList.forEach(a -> {
                for (DictStream.Target<T> target : targetList) {
                    String sourceDictCode = valueOf(target.getFunction().apply(a));
                    if(StringUtils.isBlank(sourceDictCode)) {
                        continue;
                    }
                    String[] dictCodes = StringUtils.split(sourceDictCode, SEPARATOR);
                    if(ArrayUtils.isEmpty(dictCodes)) {
                        continue;
                    } else if(dictCodes.length == 1) {
                        target.getBiConsumer().accept(a, dictMapping.get(target.getDictType() + ';' + dictCodes[0]));
                    } else {
                        StringBuilder sb = new StringBuilder();
                        for(String dictCode : dictCodes) {
                            if(sb.length() == 0) {
                                sb.append(dictMapping.get(target.getDictType() + ';' + dictCode));
                            } else {
                                sb.append(SEPARATOR).append(dictMapping.get(target.getDictType() + ';' + dictCode));
                            }
                        }
                        target.getBiConsumer().accept(a, sb.toString());
                    }
                }
            });
        } finally {
            targetList.clear();
            dictProfileList.clear();
            dictCodeList.clear();
        }
    }

    /**
     * 翻译字典 - 支持嵌套
     */
    public static void translation(Object body, BiFunction<List<String>,List<String>, Map<String, String>> biFunction){
        DictContext context = new DictContext();
        try {
            //扫描
            JSON.toJSONString(body, (ContextValueFilter) (beanContext, bean, name, value) -> {
                Field field = beanContext.getField();
                if(value != null && field.isAnnotationPresent(DictTag.class)) {
                    context.addDict(bean, new DictContext.Dict(field, name, value.toString(), SEPARATOR));
                }
                return value;
            });
            if(context.size()  == 0){
                return;
            }
            Map<String, String> dictMapping = biFunction.apply(context.getDictTypeList(), context.getDictCodeList());
            if(MapUtils.isEmpty(dictMapping)){
                return;
            }
            for(Map.Entry<Object,List<DictContext.Dict>> entry : context.entrySet()) {
                Object beanObject = entry.getKey();
                entry.getValue().forEach(dict ->{
                    //目前接口只能取到中文字典值
                    for(String dictCode : dict.getDictCodes()) {
                        dict.joinDictValue(dictMapping.get(dict.getDictType()+";"+dictCode));
                    }
                    Field field = dict.getField();
                    if(dict.isReplace()) {
                        field = ReflectionUtils.findField(beanObject.getClass(), dict.getDictName());
                    }
                    if(field != null) {
                        ReflectionUtils.setField(field, beanObject, dict.getDictValue());
                    }
                });
            }
        } finally {
            context.clear();
        }
    }

    /**
     * Serializable转String
     * @param temp
     * @return
     */
    private static String valueOf(Serializable temp) {
        if(Objects.isNull(temp)) {
            return null;
        }
        return temp.toString();
    }
}
